package com.app.core.signature;

import cn.hutool.core.util.StrUtil;
import com.app.core.mvc.servlet.CustomHttpServletRequest;
import com.app.kit.WebUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;
import java.nio.charset.StandardCharsets;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;

@Component
@Slf4j
public class SignatureInterceptor implements HandlerInterceptor {

    @Resource
    private SignatureConfig signatureConfig;

    @Override
    public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler) throws Exception {
        if (!signatureConfig.isEnabled() || HttpMethod.OPTIONS.name().equals(request.getMethod())) {
            return true;
        }
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Signature signature = handlerMethod.getBeanType().getAnnotation(Signature.class);
        if (signature == null) {
            signature = handlerMethod.getMethodAnnotation(Signature.class);
        }
        if (signature != null) {
            String path = request.getRequestURI();
            String method = request.getMethod();
            Map<String, String> headers = new HashMap();
            Enumeration enumeration = request.getHeaderNames();

            while (enumeration.hasMoreElements()) {
                String name = ((String) enumeration.nextElement()).toLowerCase();
                String value = request.getHeader(name);
                headers.put(name, value);
            }
            log.info("sign -> signature method:{}, path:{}, requestHeaders:{}", method, path, headers);
            HashMap paramsMap = new HashMap();
            try {
                if (request.getParameterMap().size() > 0) {
                    request.getParameterMap().forEach((key, valuex) -> {
                        paramsMap.put(key, valuex[0]);
                    });
                }

                SignatureDecoder signatureDecoder;
                if (StrUtil.isNotBlank(request.getContentType()) && request.getContentType().contains(MediaType.APPLICATION_JSON_VALUE)) {
                    String body = ((CustomHttpServletRequest) request).getBodyString();
                    log.info("签名body : {}", body);
                    byte[] requestBody = StringUtils.isBlank(body) ? null : body.getBytes(StandardCharsets.UTF_8);
                    signatureDecoder = new SignatureDecoder(path, method, headers, requestBody, this.signatureConfig.getKeySecretMapping(), this.signatureConfig.getVerificationTime());
                    signatureDecoder.build();
                } else {
                    signatureDecoder = new SignatureDecoder(path, method, headers, null, this.signatureConfig.getKeySecretMapping(), this.signatureConfig.getVerificationTime());
                    signatureDecoder.build(paramsMap);
                }
            } catch (Exception e) {
                log.error("server signing error, msg:{}", e.getMessage(), e);
                response.setStatus(HttpStatus.BAD_REQUEST.value());
                this.unauthorized(response, "接口验签失败");
                return false;
            }
        }
        return true;
    }

    @Override
    public void postHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler, ModelAndView modelAndView) {
    }

    @Override
    public void afterCompletion(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler, Exception ex) {
    }

    private void unauthorized(HttpServletResponse response, String errorMessage) {
        WebUtil.writeJson2Response(response, errorMessage);
    }
}
